/**
 * Copyright 2013 yangming.liu<liuyangming@gmail.com>.
 *
 * This copyrighted material is made available to anyone wishing to use, modify,
 * copy, or redistribute it subject to the terms and conditions of the GNU
 * Lesser General Public License, as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
 * for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this distribution; if not, see <http://www.gnu.org/licenses/>.
 */
package org.bytesoft.openjta.supports.internal;

import java.util.concurrent.atomic.AtomicLong;

import javax.transaction.xa.Xid;

import org.bytesoft.openjta.supports.XidFactory;
import org.bytesoft.openjta.xa.BranchXidImpl;
import org.bytesoft.openjta.xa.GlobalXidImpl;
import org.bytesoft.openjta.xa.XidImpl;
import org.bytesoft.utils.ByteUtils;

public class XidFactoryImpl implements XidFactory {
	private static final AtomicLong atomic = new AtomicLong();
	private String endpoint = "default";

	public GlobalXidImpl createGlobalXid(byte[] bytes) {
		if (bytes == null) {
			throw new IllegalArgumentException("The globalTransactionId cannot be null.");
		} else if (bytes.length > Xid.MAXGTRIDSIZE) {
			throw new IllegalArgumentException("The length of globalTransactionId cannot exceed 64 bytes.");
		}
		byte[] global = new byte[bytes.length];
		System.arraycopy(bytes, 0, global, 0, global.length);
		return new GlobalXidImpl(global);
	}

	public GlobalXidImpl createGlobalXid() {
		byte[] global = new byte[16];
		int hash = endpoint.hashCode();
		byte[] endpointBytes = ByteUtils.intToByteArray(hash);

		byte[] millisBytes = ByteUtils.longToByteArray(System.currentTimeMillis());
		int index = (int) (atomic.incrementAndGet() % Integer.MAX_VALUE);
		byte[] atomicBytes = ByteUtils.intToByteArray(index);

		System.arraycopy(endpointBytes, 0, global, 0, 4);
		System.arraycopy(millisBytes, 0, global, 4, 8);
		System.arraycopy(atomicBytes, 0, global, 12, 4);

		return new GlobalXidImpl(global);
	}

	public BranchXidImpl createBranchXid(XidImpl globalXid) {
		if (globalXid == null) {
			throw new IllegalArgumentException("Xid cannot be null.");
		} else if (globalXid.getGlobalTransactionId() == null) {
			throw new IllegalArgumentException("The globalTransactionId cannot be null.");
		} else if (globalXid.getGlobalTransactionId().length > Xid.MAXGTRIDSIZE) {
			throw new IllegalArgumentException("The length of globalTransactionId cannot exceed 64 bytes.");
		}

		byte[] global = new byte[globalXid.getGlobalTransactionId().length];
		System.arraycopy(globalXid.getGlobalTransactionId(), 0, global, 0, global.length);

		byte[] branch = new byte[16];
		int hash = endpoint.hashCode();
		byte[] endpointBytes = ByteUtils.intToByteArray(hash);
		byte[] millisBytes = ByteUtils.longToByteArray(System.currentTimeMillis());
		int index = (int) (atomic.incrementAndGet() % Integer.MAX_VALUE);
		byte[] atomicBytes = ByteUtils.intToByteArray(index);

		System.arraycopy(endpointBytes, 0, branch, 0, 4);
		System.arraycopy(millisBytes, 0, branch, 4, 8);
		System.arraycopy(atomicBytes, 0, branch, 12, 4);
		return new BranchXidImpl(global, branch);
	}

	public BranchXidImpl createBranchXid(XidImpl globalXid, byte[] branchBytes) {
		if (globalXid == null) {
			throw new IllegalArgumentException("Xid cannot be null.");
		} else if (globalXid.getGlobalTransactionId() == null) {
			throw new IllegalArgumentException("The globalTransactionId cannot be null.");
		} else if (globalXid.getGlobalTransactionId().length > Xid.MAXGTRIDSIZE) {
			throw new IllegalArgumentException("The length of globalTransactionId cannot exceed 64 bytes.");
		}

		if (branchBytes == null) {
			throw new IllegalArgumentException("The branchQulifier cannot be null.");
		} else if (branchBytes.length > Xid.MAXBQUALSIZE) {
			throw new IllegalArgumentException("The length of branchQulifier cannot exceed 64 bytes.");
		}

		byte[] global = new byte[globalXid.getGlobalTransactionId().length];
		System.arraycopy(globalXid.getGlobalTransactionId(), 0, global, 0, global.length);

		return new BranchXidImpl(global, branchBytes);
	}

	public String getEndpoint() {
		return endpoint;
	}

	public void setEndpoint(String endpoint) {
		this.endpoint = endpoint;
	}

}
